///*******************************************************************************************************
// * @brief    通用协议解析器(依赖ringbuffer模块)
// * Change Logs:
// * Date           Author         Email                   Notes
// * 2023-09-08      子恒     3393018959@qq.com      Initial version
// * 2023-09-18      子恒     3393018959@qq.com     修改接口设计，支持多协议，优化调试日志
// * 2023-10-23	     子恒     3393018959@qq.com     增加接口函数b_frame_fifo_get，用于从fifo中阻塞式获取预期长度数据
// * 2024-07-04	     子恒     3393018959@qq.com     优化帧头帧尾长度为0情况下的支持
// * 2024-07-05	     子恒     3393018959@qq.com     优化uart_get_frame_len_cb返回值为0时的处理策略
// * 2024-07-09	     子恒     3393018959@qq.com     帧头为0时,校验不通过应至少丢弃1个数据
// * 2024-07-09	     子恒     3393018959@qq.com     输入/输出缓存大小分开配置，增加调试信息等级配置
// * 2024-12-20	     子恒     3393018959@qq.com     修复对多协议的支持的相关bug
// * 2025-09-04	     子恒     3393018959@qq.com     输入输出缓冲区大小不通过宏定义配置，在初始化时进行配置，方便多协议解析器时合理利用内存
// *******************************************************************************************************/

#define _B_PROTOCOL_CORE_GLOBALS_
#include "b_protocol_core.h"

#define IDIE_FRAME_FACTOR (10000000ul * (IDIE_FACTOR + 1) / FRAME_BAUD_RATE / IDIE_TIMER_US)

#if (EN_FRAME_DEBUG != 0)
static void printHexAscii(b_frame_type *pframe, const unsigned char *data, size_t length)
{
  for (size_t i = 0; i < length; i++)
  {
    FRAME_RAW_INFO_PRINTF(pframe, "%02X ", data[i]);
  }
  FRAME_RAW_INFO_PRINTF(pframe, "\n");
}
#endif

void b_frame_idie_timer(b_frame_type *pframe)
{
  pframe->_idie_timer++;
  pframe->_systick++;
}

uint8_t b_frame_init(b_frame_type *pframe, b_frame_init_type *pframeinit)
{
  uint8_t err = 0;
  if ((!pframeinit) || (!pframe) || (!pframeinit->in_frame_buffer) || (!pframeinit->out_frame_buffer))
  {
    return B_ERROR;
  }

  if (pframeinit->head == NULL)
    pframeinit->head_len = 0;
  if (pframeinit->end == NULL)
    pframeinit->end_len = 0;
  pframe->frame_init.pname = pframeinit->pname;
  pframe->frame_init.check_cb = pframeinit->check_cb;
  pframe->frame_init.end = pframeinit->end;
  pframe->frame_init.end_len = pframeinit->end_len;
  pframe->frame_init.get_frame_len_cb = pframeinit->get_frame_len_cb;
  pframe->frame_init.head = pframeinit->head;
  pframe->frame_init.head_len = pframeinit->head_len;
  pframe->frame_init.in_frame_buffer = pframeinit->in_frame_buffer;
  pframe->frame_init.out_frame_buffer = pframeinit->out_frame_buffer;
  pframe->frame_init.in_buffer_len = pframeinit->in_buffer_len;
  pframe->frame_init.out_buffer_len = pframeinit->out_buffer_len;
  pframe->frame_init.log_level = pframeinit->log_level;
  pframe->head_match_flg = 0;

  err = ring_buf_init(&pframe->_frame_ring, pframe->frame_init.in_frame_buffer, pframe->frame_init.in_buffer_len);
  b_frame_fifo_clear(pframe);
  if (err)
    return B_SUCCESS;
  FRAME_LOG_INFO_PRINTF(pframe, "%s-err:ring_buf_init err\r\n", pframe->frame_init.pname);
  return B_ERROR;
}

uint8_t b_frame_put(b_frame_type *pframe, uint8_t *dat, uint32_t len)
{
  if (!dat || len == 0)
  {
    return B_ERROR;
  }

  pframe->_idie_timer = 0;

  uint8_t putlen = ring_buf_put(&pframe->_frame_ring, dat, len);

  if (putlen != len)
  {
    ring_buf_clr(&pframe->_frame_ring);
    FRAME_LOG_INFO_PRINTF(pframe, "%s-err:The ringbuffer is full\r\n", pframe->frame_init.pname);
    return B_ERROR;
  }

  return B_SUCCESS;
}

void b_frame_fifo_clear(b_frame_type *pframe)
{
  if (!pframe)
  {
    FRAME_LOG_INFO_PRINTF(pframe, "%s-err:pframe is null\r\n", pframe->frame_init.pname);
    return;
  }
  ring_buf_clr(&pframe->_frame_ring);
}

uint32_t b_frame_fifo_get(b_frame_type *pframe, uint8_t *dat, uint32_t len, uint32_t timeout)
{
  uint8_t ring_len;
  uint32_t start_ticks = pframe->_systick;
  uint32_t elapsed_ticks;

  if (!dat)
  {
    FRAME_LOG_INFO_PRINTF(pframe, "%s-err:b_frame_fifo_get dat is null\r\n", pframe->frame_init.pname);
    return 0;
  }
  do
  {
    ring_len = ring_buf_len(&pframe->_frame_ring);

    if (ring_len < len)
    {
      if (pframe->_systick >= start_ticks)
      {
        elapsed_ticks = pframe->_systick - start_ticks;
      }
      else
      {
        elapsed_ticks = 0xffffffff - pframe->_systick + start_ticks;
      }
      elapsed_ticks = elapsed_ticks * 1000ul / IDIE_TIMER_US;
    }
    else
    {
      FRAME_LOG_INFO_PRINTF(pframe, "%s-successful:b_frame_fifo_get>>> ring_len = %d,len = %d\r\n", pframe->frame_init.pname, ring_len, len);
      break;
    }
  } while (elapsed_ticks < timeout);

  if (elapsed_ticks >= timeout)
  {
    FRAME_LOG_INFO_PRINTF(pframe, "%s-err:b_frame_fifo_get>>> timeout %d\r\n", pframe->frame_init.pname, elapsed_ticks);
  }

  uint8_t getlen = ring_buf_get(&pframe->_frame_ring, dat, len);
  return getlen;
}

static uint8_t b_check_head(b_frame_type *pframe)
{
  if (pframe->frame_init.head_len == 0)
    return B_SUCCESS;

  uint8_t i = 0;
  uint8_t tmp;
  uint16_t len = ring_buf_len(&pframe->_frame_ring);

  if (len < (pframe->frame_init.head_len + pframe->frame_init.end_len + 1))
  {
    return B_ERROR;
  }

  do
  {
    if (!ring_buf_get(&pframe->_frame_ring, &tmp, 1))
    {
      break;
    }
    FRAME_LOG_INFO_PRINTF(pframe, "%s-info:head byte[%d]=0x%x,match=0x%x\r\n", pframe->frame_init.pname, i, tmp, pframe->frame_init.head[i]);
    if (tmp == pframe->frame_init.head[i])
    {
      if (++i == pframe->frame_init.head_len)
      {
        return B_SUCCESS;
      }
    }
    else
    {
      i = 0;
    }
  } while (1);

  return B_ERROR;
}

const uint8_t *b_frame_check_get(b_frame_type *pframe, uint16_t *len)
{
  uint16_t i, j;
  uint16_t unhead_frame_len;

  if (!pframe->head_match_flg)
  {
    uint8_t err = b_check_head(pframe);
    if (err != B_SUCCESS)
    {
      return NULL;
    }
    if (pframe->frame_init.head_len != 0)
    {
      memcpy(pframe->frame_init.out_frame_buffer, pframe->frame_init.head, pframe->frame_init.head_len);
    }
    pframe->head_match_flg = 1;
    FRAME_LOG_INFO_PRINTF(pframe, "%s-successful:Frame header matching\r\n", pframe->frame_init.pname);
  }

  if (pframe->frame_init.get_frame_len_cb != NULL)
  {
    uint16_t data_len = ring_buf_check_get(&pframe->_frame_ring, &pframe->frame_init.out_frame_buffer[pframe->frame_init.head_len], pframe->frame_init.out_buffer_len);
    unhead_frame_len = pframe->frame_init.get_frame_len_cb(pframe->frame_init.out_frame_buffer, data_len + pframe->frame_init.head_len);

    if (unhead_frame_len >= pframe->frame_init.head_len)
    {
      unhead_frame_len = unhead_frame_len - pframe->frame_init.head_len;
    }
    FRAME_LOG_INFO_PRINTF(pframe, "%s-info:unhead_frame_len = %d,ring_data_len = %d\r\n", pframe->frame_init.pname, unhead_frame_len, data_len);
    if ((data_len < unhead_frame_len) || (unhead_frame_len == 0))
    {
      if (pframe->_idie_timer > IDIE_FRAME_FACTOR * (unhead_frame_len + 1))
      {
        FRAME_LOG_INFO_PRINTF(pframe, "%s-err:idie_timer timeout %d\r\n", pframe->frame_init.pname, pframe->_idie_timer);
        pframe->_idie_timer = 0;
        pframe->head_match_flg = 0;
        if (pframe->frame_init.head_len == 0)
        {
          ring_buf_clr_len(&pframe->_frame_ring, 1);
        }
      }
      else if (unhead_frame_len > pframe->frame_init.out_buffer_len)
      {
        FRAME_LOG_INFO_PRINTF(pframe, "%s-err:Output buffer length is too small.. %d %d\r\n", pframe->frame_init.pname, unhead_frame_len, pframe->frame_init.out_buffer_len);
        pframe->head_match_flg = 0;
        ring_buf_clr_len(&pframe->_frame_ring, 1);
      }
      return NULL;
    }
  }
  else
  {
    FRAME_LOG_INFO_PRINTF(pframe, "%s-err:get_frame_len_cb is null\r\n", pframe->frame_init.pname);
    return NULL;
  }

  i = unhead_frame_len + pframe->frame_init.head_len - pframe->frame_init.end_len;

  for (j = 0; j < pframe->frame_init.end_len;)
  {
    if (pframe->frame_init.out_frame_buffer[i] == pframe->frame_init.end[j])
    {
      i++;
      j++;
    }
    else
    {
      break;
    }
  }

  FRAME_LOG_INFO_PRINTF(pframe, "%s-info:data>>>\r\n", pframe->frame_init.pname);
#if (EN_FRAME_DEBUG != 0)
  printHexAscii(pframe, pframe->frame_init.out_frame_buffer, pframe->frame_init.head_len + unhead_frame_len);
#endif

  if (j == pframe->frame_init.end_len)
  {
    *len = pframe->frame_init.head_len + unhead_frame_len;
    if (pframe->frame_init.check_cb != NULL)
    {
      uint8_t err = pframe->frame_init.check_cb(pframe->frame_init.out_frame_buffer, *len);
      if (err == B_SUCCESS)
      {
        pframe->head_match_flg = 0;
        ring_buf_clr_len(&pframe->_frame_ring, unhead_frame_len);
        FRAME_LOG_INFO_PRINTF(pframe, "%s-successful:Frame all matching\r\n", pframe->frame_init.pname);
        return pframe->frame_init.out_frame_buffer;
      }
      else
      {
        if (pframe->frame_init.head_len == 0)
        {
          ring_buf_clr_len(&pframe->_frame_ring, 1);
        }
        FRAME_LOG_INFO_PRINTF(pframe, "%s-err:Frame check err\r\n", pframe->frame_init.pname);
      }
    }
  }
  pframe->head_match_flg = 0;
  return NULL;
}
